#pragma once
#include "log.hpp"
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <memory>
#include <string>
#include <thread>
namespace MY
{
    const int dpnum = 5;
    template <typename T>
    class pthreadPool
    {
    private:
        int _num;
        std::vector<std::thread> _threads;
        std::queue<T> _taskq;
        std::condition_variable _cv;
        std::mutex _mtx;
        bool _isrunning;
        int _sleeperNum;
        pthreadPool(int num = dpnum)
            : _num(num)
            ,_isrunning(true)
            ,_sleeperNum(0)
        {
            for (int i = 0; i < num; i++)
            {
                LOG(MY::logLevel::INFO) << "create a thread";
                _threads.emplace_back([this]()
                                      { handleerTask(); });
            }
        }
        static std::unique_ptr<pthreadPool<T>>instance;
        static std::mutex gmtx;
        pthreadPool(const pthreadPool&)=delete;
        pthreadPool<T>&operator=(const pthreadPool<T>&)=delete;
    public:
        ~pthreadPool()
        {
        }
        static pthreadPool<T>*getInstance(int num=dpnum)
        {
            if(instance==nullptr)
            {
                std::unique_lock<std::mutex>lock(gmtx);
                if(instance==nullptr)
                {
                    instance=std::unique_ptr<pthreadPool<T>>(new pthreadPool<T>(num));
                    LOG(logLevel::INFO)<<"create an instance";
                }
            }
            LOG(MY::logLevel::INFO)<<"get an instance";
            return instance.get();
        }

        void handleerTask()
        {
            LOG(MY::logLevel::INFO) << "The " << std::this_thread::get_id() << " thread is running";
            while (true)
            {
                T t;
                {
                    std::unique_lock<std::mutex> lock(_mtx);
                    while (_taskq.empty() && _isrunning)
                    {
                        _sleeperNum++;
                        _cv.wait(lock);
                        _sleeperNum--;
                    }
                    if (_taskq.empty() && !_isrunning)
                    {
                        LOG(MY::logLevel::INFO)<< "The " << std::this_thread::get_id() << " thread quit";
                        break;
                    }
                    LOG(MY::logLevel::INFO) << "The " << std::this_thread::get_id() << " thread get a task";
                    // sleep(3);
                    t = _taskq.front();
                    _taskq.pop();
                }
                t();
            }
        }
        void stop()
        {
            std::lock_guard<std::mutex> lock(_mtx);
            if(!_isrunning)return;
            _isrunning=false;
            if(_sleeperNum)
                _cv.notify_all();
            LOG(MY::logLevel::INFO)<<"wake up all threads";
        }   
        void joinAll()
        {
            for(auto& thread:_threads)
            {
                thread.join();
                LOG(MY::logLevel::INFO)<< "The " << std::this_thread::get_id() << " thread joined";
            }
        }
        bool enqueue(const T& task)
        {
            std::unique_lock<std::mutex>lock;
            if(_isrunning)
            {
                _taskq.push(task);
                if(_sleeperNum)
                    _cv.notify_one();
                LOG(MY::logLevel::INFO)<<"task insert successfully";
                return true;
            }
            return false;
        }
    };
    template<class T>
    std::unique_ptr<pthreadPool<T>>pthreadPool<T>::instance=nullptr;
    template<class T>
    std::mutex pthreadPool<T>::gmtx;
}